"""Model health check API endpoints."""

from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
from pydantic import BaseModel, Field
import asyncio
import logging
from motor.motor_asyncio import AsyncIOMotorDatabase
import redis.asyncio as redis
import json

from ..core.auth import get_current_user
from ..core.database import get_db
from ..core.cache import get_redis
from ..models.health_checker import ModelHealthChecker
from ..models.config_manager import ConfigManager

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/v1/models/health", tags=["model-health"])

# Cache TTL in seconds
HEALTH_CACHE_TTL = 300  # 5 minutes


class HealthCheckResult(BaseModel):
    """Health check result for a single model."""
    
    model_id: str
    status: str = Field(..., pattern="^(healthy|unhealthy|checking|unknown)$")
    response_time: Optional[int] = Field(None, description="Response time in milliseconds")
    last_check: datetime
    error: Optional[str] = None
    details: Dict[str, Any] = Field(default_factory=dict)
    
    class Config:
        json_encoders = {
            datetime: lambda v: v.isoformat()
        }


class BatchHealthCheckRequest(BaseModel):
    """Request for batch health check."""
    
    model_ids: List[str] = Field(..., min_items=1, max_items=50)
    force_check: bool = Field(default=False, description="Force check even if cached")


class BatchHealthCheckResponse(BaseModel):
    """Response for batch health check."""
    
    results: List[HealthCheckResult]
    from_cache: bool
    timestamp: datetime
    
    class Config:
        json_encoders = {
            datetime: lambda v: v.isoformat()
        }


async def get_cached_health_status(
    redis_client: redis.Redis,
    model_id: str
) -> Optional[HealthCheckResult]:
    """Get cached health status for a model."""
    try:
        cache_key = f"model_health:{model_id}"
        cached = await redis_client.get(cache_key)
        
        if cached:
            data = json.loads(cached)
            # Convert timestamp string back to datetime
            data["last_check"] = datetime.fromisoformat(data["last_check"])
            return HealthCheckResult(**data)
            
    except Exception as e:
        logger.warning(f"Failed to get cached health status for {model_id}: {e}")
    
    return None


async def set_cached_health_status(
    redis_client: redis.Redis,
    model_id: str,
    result: HealthCheckResult
) -> None:
    """Cache health status for a model."""
    try:
        cache_key = f"model_health:{model_id}"
        # Convert datetime to string for JSON serialization
        data = result.dict()
        data["last_check"] = data["last_check"].isoformat()
        
        await redis_client.setex(
            cache_key,
            HEALTH_CACHE_TTL,
            json.dumps(data)
        )
        
    except Exception as e:
        logger.warning(f"Failed to cache health status for {model_id}: {e}")


async def perform_health_check(
    db: AsyncIOMotorDatabase,
    model_id: str,
    user_id: str
) -> HealthCheckResult:
    """Perform actual health check for a model."""
    try:
        # Get model configuration
        config = await db.model_configs.find_one({
            "id": model_id,
            "user_id": user_id
        })
        
        if not config:
            return HealthCheckResult(
                model_id=model_id,
                status="unknown",
                last_check=datetime.utcnow(),
                error="Model configuration not found"
            )
        
        # Initialize health checker
        health_checker = ModelHealthChecker()
        
        # Perform health check based on provider
        start_time = asyncio.get_event_loop().time()
        
        # 使用统一的健康检查方法
        result = await health_checker.check_model_health(
            base_url=config.get("api_base", ""),
            api_key=config.get("api_key", ""),
            model_name=config.get("model_name", ""),
            provider=config.get("provider", "")
        )
        is_healthy = result.get("status") == "available"
        
        end_time = asyncio.get_event_loop().time()
        response_time = int((end_time - start_time) * 1000)  # Convert to milliseconds
        
        result = HealthCheckResult(
            model_id=model_id,
            status="healthy" if is_healthy else "unhealthy",
            response_time=response_time,
            last_check=datetime.utcnow(),
            error=None if is_healthy else "Health check failed",
            details={
                "provider": config["provider"],
                "model_name": config.get("model_name", "")
            }
        )
        
        # Store health check history
        await db.health_check_history.insert_one({
            "model_id": model_id,
            "user_id": user_id,
            "status": result.status,
            "response_time": response_time,
            "timestamp": result.last_check,
            "error": result.error
        })
        
        return result
        
    except Exception as e:
        logger.error(f"Health check failed for model {model_id}: {e}")
        return HealthCheckResult(
            model_id=model_id,
            status="unhealthy",
            last_check=datetime.utcnow(),
            error=str(e)
        )


@router.get("", response_model=BatchHealthCheckResponse)
async def batch_health_check(
    force_check: bool = False,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db),
    redis_client: redis.Redis = Depends(get_redis)
) -> BatchHealthCheckResponse:
    """
    Get health status for all user's models.
    
    Args:
        force_check: Force check even if cached
    
    Returns:
        Batch health check results
    """
    try:
        # Get all user's model configurations
        configs = await db.model_configs.find(
            {"user_id": current_user["sub"], "enabled": True}
        ).to_list(None)
        
        if not configs:
            return BatchHealthCheckResponse(
                results=[],
                from_cache=True,
                timestamp=datetime.utcnow()
            )
        
        results = []
        from_cache = not force_check
        
        # Check each model
        tasks = []
        for config in configs:
            model_id = config["id"]
            
            if not force_check:
                # Try to get cached result first
                cached = await get_cached_health_status(redis_client, model_id)
                if cached:
                    results.append(cached)
                    continue
            
            # Perform actual health check
            from_cache = False
            task = perform_health_check(db, model_id, current_user["sub"])
            tasks.append(task)
        
        # Wait for all health checks to complete
        if tasks:
            check_results = await asyncio.gather(*tasks)
            
            # Cache results
            for result in check_results:
                await set_cached_health_status(redis_client, result.model_id, result)
                results.append(result)
        
        return BatchHealthCheckResponse(
            results=results,
            from_cache=from_cache,
            timestamp=datetime.utcnow()
        )
        
    except Exception as e:
        logger.error(f"Batch health check failed: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to perform batch health check"
        )


@router.post("/{model_id}", response_model=HealthCheckResult)
async def single_health_check(
    model_id: str,
    force_check: bool = False,
    background_tasks: BackgroundTasks = BackgroundTasks(),
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db),
    redis_client: redis.Redis = Depends(get_redis)
) -> HealthCheckResult:
    """
    Check health for a specific model.
    
    Args:
        model_id: Model configuration ID
        force_check: Force check even if cached
    
    Returns:
        Health check result
    """
    try:
        # Check if model belongs to user
        config = await db.model_configs.find_one({
            "id": model_id,
            "user_id": current_user["sub"]
        })
        
        if not config:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Model configuration not found"
            )
        
        # Try to get cached result if not forcing
        if not force_check:
            cached = await get_cached_health_status(redis_client, model_id)
            if cached:
                return cached
        
        # Perform health check
        result = await perform_health_check(db, model_id, current_user["sub"])
        
        # Cache result
        await set_cached_health_status(redis_client, model_id, result)
        
        return result
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Single health check failed for model {model_id}: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to perform health check"
        )


@router.post("/batch", response_model=BatchHealthCheckResponse)
async def custom_batch_health_check(
    request: BatchHealthCheckRequest,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db),
    redis_client: redis.Redis = Depends(get_redis)
) -> BatchHealthCheckResponse:
    """
    Check health for specific models.
    
    Args:
        request: Batch health check request with model IDs
    
    Returns:
        Batch health check results
    """
    try:
        # Verify all models belong to user
        configs = await db.model_configs.find({
            "id": {"$in": request.model_ids},
            "user_id": current_user["sub"]
        }).to_list(None)
        
        found_ids = {config["id"] for config in configs}
        missing_ids = set(request.model_ids) - found_ids
        
        if missing_ids:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f"Model configurations not found: {list(missing_ids)}"
            )
        
        results = []
        from_cache = not request.force_check
        
        # Check each model
        tasks = []
        for model_id in request.model_ids:
            if not request.force_check:
                # Try to get cached result first
                cached = await get_cached_health_status(redis_client, model_id)
                if cached:
                    results.append(cached)
                    continue
            
            # Perform actual health check
            from_cache = False
            task = perform_health_check(db, model_id, current_user["sub"])
            tasks.append(task)
        
        # Wait for all health checks to complete
        if tasks:
            check_results = await asyncio.gather(*tasks)
            
            # Cache results
            for result in check_results:
                await set_cached_health_status(redis_client, result.model_id, result)
                results.append(result)
        
        return BatchHealthCheckResponse(
            results=results,
            from_cache=from_cache,
            timestamp=datetime.utcnow()
        )
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Custom batch health check failed: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to perform batch health check"
        )


@router.get("/{model_id}/history", response_model=List[Dict[str, Any]])
async def get_health_check_history(
    model_id: str,
    limit: int = 100,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db)
) -> List[Dict[str, Any]]:
    """
    Get health check history for a model.
    
    Args:
        model_id: Model configuration ID
        limit: Maximum number of records to return
    
    Returns:
        Health check history
    """
    try:
        # Verify model belongs to user
        config = await db.model_configs.find_one({
            "id": model_id,
            "user_id": current_user["sub"]
        })
        
        if not config:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Model configuration not found"
            )
        
        # Get history
        history = await db.health_check_history.find(
            {"model_id": model_id, "user_id": current_user["sub"]}
        ).sort("timestamp", -1).limit(limit).to_list(None)
        
        # Convert ObjectId to string and datetime to ISO format
        for record in history:
            record["_id"] = str(record["_id"])
            record["timestamp"] = record["timestamp"].isoformat()
        
        return history
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to get health check history for model {model_id}: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to retrieve health check history"
        )


@router.delete("/{model_id}/cache", status_code=status.HTTP_204_NO_CONTENT)
async def clear_health_cache(
    model_id: str,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_db),
    redis_client: redis.Redis = Depends(get_redis)
) -> None:
    """
    Clear cached health status for a model.
    
    Args:
        model_id: Model configuration ID
    """
    try:
        # Verify model belongs to user
        config = await db.model_configs.find_one({
            "id": model_id,
            "user_id": current_user["sub"]
        })
        
        if not config:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Model configuration not found"
            )
        
        # Clear cache
        cache_key = f"model_health:{model_id}"
        await redis_client.delete(cache_key)
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to clear health cache for model {model_id}: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Failed to clear health cache"
        )